package com.dreamlin.gankio.common.exception.rxweaver.core

import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.Single
import io.reactivex.annotations.NonNull
import io.reactivex.functions.Function
import org.reactivestreams.Publisher
import java.util.concurrent.TimeUnit

class RetryConfig(
    var maxRetries: Int = 1,
    var delay: Long = 1000,
    var retryCondition: Suppiler<Single<Boolean>> = object : Suppiler<Single<Boolean>> {
        override fun call(): Single<Boolean> {
            return Single.just(false)
        }

    }
)

class FlowableRetryDelay(private val provider: Function<Throwable, RetryConfig>) :
    Function<Flowable<Throwable>, Publisher<*>> {

    private var retryCount: Int = 0

    init {
        if (provider == null)
            throw NullPointerException("The parameter provider can't be null!")
    }

    @Throws(Exception::class)
    override fun apply(@NonNull throwableFlowable: Flowable<Throwable>): Publisher<*> {
        return throwableFlowable
            .flatMap { throwable ->
                val retryConfig = provider.apply(throwable)
                val delay = retryConfig.delay

                if (++retryCount <= retryConfig.maxRetries) {

                    retryConfig
                        .retryCondition
                        .call()
                        .flatMapPublisher { t ->
                            if (t)
                                Flowable.timer(delay, TimeUnit.MILLISECONDS)
                            else
                                Flowable.error<Any>(throwable)
                        }
                } else Flowable.error<Any>(throwable)
            }
    }
}

class ObservableRetryDelay(@param:NonNull private val provider: Function<Throwable, RetryConfig>) :
    Function<Observable<Throwable>, ObservableSource<*>> {

    private var retryCount: Int = 0

    init {
        if (provider == null)
            throw NullPointerException("The parameter provider can't be null!")
    }

    @Throws(Exception::class)
    override fun apply(throwableObservable: Observable<Throwable>): ObservableSource<*> {
        return throwableObservable
            .flatMap { throwable ->
                val retryConfig = provider.apply(throwable)
                val delay = retryConfig.delay

                if (++retryCount <= retryConfig.maxRetries) {
                    retryConfig
                        .retryCondition
                        .call()
                        .flatMapObservable { t ->
                            if (t)
                                Observable.timer(delay, TimeUnit.MILLISECONDS)
                            else
                                Observable.error<Any>(throwable)
                        }
                } else Observable.error<Any>(throwable)
            }
    }
}


